<?php
namespace TennisTournament\Service;

use TennisTournament\Model\Group;
use TennisTournament\Model\GroupStats;
use TennisTournament\Model\WinnerEnum;
use TennisTournament\Persistent\MatchDao;

class CommonServiceManager implements CommonService {

    /** @var MatchDao */
    protected $matchDao;

    public function __construct(MatchDao $matchDao) {
        $this->matchDao = $matchDao;
    }

    /*
     * Sorted {@link GroupStats} into there.</br>
     * First will be sort the {@Player} with most points.</br>
     * After this will be sort the {@Player} with most positive difference,
     * between takenPoints and getPoints.
     * @return array
     */
    public function sortGroupStats(Group& $group) {
        $collectionGroupStats = $group->getStats();
        $collectionMatches = $this->matchDao->getAllMatchesBy($group);
        $aux = array();

        $this->sort($collectionGroupStats, 0, count($collectionGroupStats) - 1, $collectionMatches, $aux);

        $group->setStats($collectionGroupStats);
    }

    private function sort(array& $collectionGroupStats, $intLo, $intHi, array& $collectionMatchesPerGroup, array& $aux) {
        // Sort $collectionGroupStats[hi..lo]
        if ($intHi <= $intLo) return;
        $intMid = (int)($intLo + ($intHi - $intLo) / 2);
        $this->sort($collectionGroupStats, $intLo, $intMid, $collectionMatchesPerGroup, $aux); // Sort left half
        $this->sort($collectionGroupStats, $intMid + 1, $intHi, $collectionMatchesPerGroup, $aux); // Sort right half
        $this->merge($collectionGroupStats, $intLo, $intMid, $intHi, $collectionMatchesPerGroup, $aux); // Merge results
    }

    private function merge(array& $collectionGroups, $intLo, $intMid, $intHi, array& $collectionMatchesPerGroup, array& $aux) {
        $intI = $intLo;
        $intJ = $intMid + 1;

        for ($intK = $intLo; $intK <= $intHi; $intK++) { // Copy a[lo..hi] to aux[lo..hi]
            $aux[$intK] = $collectionGroups[$intK];
        }

        for ($intK = $intLo; $intK <= $intHi; $intK++) { // Merge back to a[lo..hi].
            if ($intI > $intMid)        $collectionGroups[$intK] = $aux[$intJ++];
            else if ($intJ > $intHi)    $collectionGroups[$intK] = $aux[$intI++];
            else {
                $isLeftPlayerWinner = false;

                /** @var Match $match */
                foreach ($collectionMatchesPerGroup as $match) {
                     if (($match->getPlayerGuest()->getId() == $aux[$intJ]->getPlayer()->getId()
                         || $match->getPlayerHost()->getId() == $aux[$intJ]->getPlayer()->getId()) &&
                         ($match->getPlayerGuest()->getId() == $aux[$intI]->getPlayer()->getId()
                             || $match->getPlayerHost()->getId() == $aux[$intI]->getPlayer()->getId())) {

                         if (($match->getPlayerHost()->getId() == $aux[$intI]->getPlayer()->getId() && $match->getWinner() == WinnerEnum::HOST) ||
                             ($match->getPlayerGuest()->getId() == $aux[$intI]->getPlayer()->getId() && $match->getWinner() == WinnerEnum::GUEST)) {
                             $isLeftPlayerWinner = true;
                         }
                         break;
                     }
                }

                if ($this->isBigger($aux[$intI], $aux[$intJ], $isLeftPlayerWinner)) {
                    $collectionGroups[$intK] = $aux[$intI++];
                } else {
                     $collectionGroups[$intK] = $aux[$intJ++];
                }
            }
        }
    }

    private function isBigger(GroupStats $a, GroupStats $b, $isLeftPlayerAWinner) {
        if ($a->getPoints() > $b->getPoints()) {
            return true;
        } else if ($a->getPoints() < $b->getPoints()) {
            return false;
        } else {
            $differencePointsOfA = $a->getTakenPoints() - $a->getGetPoints();
            $differencePointsOfB = $b->getTakenPoints() - $b->getGetPoints();

            if ($differencePointsOfA > $differencePointsOfB) {
                return true;
            } else if ($differencePointsOfA < $differencePointsOfB) {
                return false;
            } else {
                return $isLeftPlayerAWinner ? true : false;
            }
        }
    }
}